package com.fm.financemanage.filter;

import com.alibaba.fastjson.JSONObject;

import com.fm.financemanage.component.RedisComponent;
import com.fm.financemanage.component.SpringContextHolder;
import com.fm.financemanage.config.RedisSerializerConfig;
import com.fm.financemanage.constant.AppHttpCodeEnum;
import com.fm.financemanage.model.JWTToken;
import com.fm.financemanage.response.Callback;
import com.fm.financemanage.response.JsonData;
import com.fm.financemanage.utils.TextUtil;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;

/**
 * 自定义的过滤器
 **/
@Component
public class MyAuthenticationFilter extends FormAuthenticationFilter {

    private static final int REFRESH_TIME = 600;

    private static final String AUTHORIZATION = "Authorization:";

    @Autowired
    private RedisComponent redisComponent;

    /**
     * isAccessAllowed 方法是每次当执行需要 authc 的方法时 都会被执行
     * 如果返回false 则会执行 onAccessDenied
     * 如果返回true  则会执行 onLoginSuccess
     * 我们在isAccessAllowed 获取到token,并且封装成自定义的JWTToken,
     * 并执行 shiro 的 login(会去调用我们自定义的realm中的doGetAuthenticationInfo),这时候我们就可以在此方法中进行处理
     **/
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        if (request instanceof HttpServletRequest) {
            if ("OPTIONS".equals(((HttpServletRequest) request).getMethod().toUpperCase())) {
                return true;
            }
            HttpServletRequest request1 = (HttpServletRequest) request;
            String authorization = request1.getHeader("Authorization");
            if (TextUtil.isEmpty(authorization)) {
                return false;
            }
            JWTToken jwtToken = new JWTToken(authorization);
            try {
                Subject subject = getSubject(request, response);
                subject.login(jwtToken);
                return this.onLoginSuccess(jwtToken, subject, request, response);
            } catch (Exception exception) {
                exception.printStackTrace();
                return false;
            }
        }
        return false;
    }

    /**
     * 在登录成功后执行刷新token操作
     *
     * @return boolean
     * @author zpc
     * @date 15:04 2021/6/15
     **/
    @SuppressWarnings("all")
    @Override
    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject,
                                     ServletRequest request, ServletResponse response) {
        try {
            HttpServletRequest otherRequest = (HttpServletRequest) request;
            HttpServletResponse otherResponse = (HttpServletResponse) response;
            String authorization = otherRequest.getHeader("Authorization");
            if (!TextUtil.isEmpty(authorization)) {

                RedisTemplate redisTemplate = SpringContextHolder.getBean(RedisTemplate.class);
                RedisSerializerConfig.setSerializer(redisTemplate);
                Long expire = redisTemplate.opsForValue().getOperations().getExpire(AUTHORIZATION+authorization);
                if(expire==-2){
                    //token过期重新登录
                    return false;
                }
                if (expire!=-1&&expire < REFRESH_TIME ) {
                    //刷新
                    redisTemplate.opsForValue().set(AUTHORIZATION + authorization, 1, 3600 * 10);
                    otherResponse.setHeader("Authorization", authorization);
                }
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;

    }

    /**
     * 在登录失败后返回错误信息交由前端进行处理
     *
     * @return boolean
     * @author zpc
     * @date 15:04 2021/6/15
     **/
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response)
            throws Exception {
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        httpServletResponse.setStatus(200);
        httpServletResponse.setContentType("application/json;charset=utf-8");
        PrintWriter out = httpServletResponse.getWriter();
        JsonData msg = Callback.fail(AppHttpCodeEnum.TOKEN_EXPIRE.getCode() + "", AppHttpCodeEnum.TOKEN_EXPIRE.getErrorMessage());
        out.println(JSONObject.toJSONString(msg));
        out.flush();
        out.close();
        return false;

    }
}